"""内容发布相关路由"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from sqlalchemy.orm import Session
import json
import logging
import random
import time
from typing import Optional, List, Dict
from datetime import datetime

from database import get_db
from models.models import Content, User, Interaction
from patterns.observer import EventManager, Event, EventType
from utils.myredis import r
from routers.dependencies import get_current_user, get_current_user_optional

# 先创建logger
logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/content", tags=["内容"])


@router.post("/publish")
async def publish_content(
    body: dict,
    current_user: Optional[User] = Depends(get_current_user_optional),
    db: Session = Depends(get_db)
):
    """
    发布内容
    
    参数:
        title: 标题
        content: 内容
        media_urls: 媒体URL列表
        media_type: 媒体类型（1=图文，2=视频，3=直播）
        tags: 标签（逗号分隔）
        location: 地点
    """
    try:
        # 获取用户ID（如果已登录则使用，否则返回错误）
        if current_user:
            user_id = current_user.id
        else:
            # 如果没有登录，返回错误
            return JSONResponse({
                "success": False,
                "message": "请先登录"
            }, status_code=401)
        
        # 解析参数
        title = body.get('title', '')
        content_text = body.get('content', '')
        media_urls = body.get('media_urls', [])
        media_type = body.get('media_type', 1)
        tags_str = body.get('tags', '')
        location = body.get('location', '')
        
        # 解析标签
        tags = []
        if tags_str:
            tags = [tag.strip() for tag in tags_str.split(',') if tag.strip()]
        
        # 如果有标题或内容，将其中的标签提取出来
        if not tags:
            import re
            hashtags = re.findall(r'#(\w+)', title + ' ' + content_text)
            tags = hashtags
        
        # 创建内容
        new_content = Content(
            user_id=user_id,
            title=title or None,
            content=content_text,
            media_urls=json.dumps(media_urls) if media_urls else None,
            media_type=media_type,
            tags=json.dumps(tags) if tags else None,
            location=location if location else None,
            like_count=0,
            view_count=0,
            comment_count=0,
            collect_count=0,
            status=1,
            created_at=datetime.now()
        )
        
        db.add(new_content)
        db.commit()
        db.refresh(new_content)
        
        logger.info(f"用户 {user_id} 发布内容成功: {new_content.id}")
        
        # 清除相关缓存（内容列表缓存）
        try:
            cache_pattern = "cache:content:list:*"
            keys = r.get_keys(cache_pattern)
            for key in keys:
                r.str_del(key.decode() if isinstance(key, bytes) else key)
            logger.debug(f"已清除 {len(keys)} 个内容列表缓存")
        except Exception as e:
            logger.warning(f"清除缓存失败: {e}")
        
        # === 使用观察者模式发布事件 ===
        event_manager = EventManager()
        event = Event(
            event_type=EventType.CONTENT_PUBLISHED,
            data={
                'content': new_content,
                'db': db,
                'user_id': user_id
            },
            user_id=user_id
        )
        event_manager.publish(event)
        
        return JSONResponse({
            "success": True,
            "message": "发布成功",
            "content_id": new_content.id
        })
        
    except Exception as e:
        db.rollback()
        logger.error(f"发布内容失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"发布失败: {str(e)}"
        }, status_code=500)


def get_contents_from_db(db: Session, limit: int, offset: int) -> List[Content]:
    """从数据库查询内容列表"""
    return db.query(Content).filter(
        Content.status == 1
    ).order_by(
        Content.created_at.desc()
    ).limit(limit).offset(offset).all()


def format_content_result(content: Content, author: Optional[User]) -> Dict:
    """格式化内容数据为字典"""
    # 解析媒体URLs和标签
    media_urls = []
    if content.media_urls:
        if isinstance(content.media_urls, list):
            media_urls = content.media_urls
        elif isinstance(content.media_urls, str):
            try:
                media_urls = json.loads(content.media_urls)
            except:
                media_urls = []
    
    tags = []
    if content.tags:
        if isinstance(content.tags, list):
            tags = content.tags
        elif isinstance(content.tags, str):
            try:
                tags = json.loads(content.tags)
            except:
                tags = []
    
    return {
        "id": content.id,
        "user_id": content.user_id,
        "title": content.title or '无标题',
        "content": content.content or '',
        "media_urls": media_urls,
        "tags": tags,
        "location": content.location,
        "like_count": content.like_count or 0,
        "view_count": content.view_count or 0,
        "collect_count": content.collect_count or 0,
        "comment_count": content.comment_count or 0,
        "created_at": content.created_at.isoformat() if content.created_at else None,
        "author_nickname": author.nickname if author else '游客',
        "author_avatar": author.avatar_url if author else None
    }


def get_content_list_with_cache(
    db: Session,
    limit: int = 20,
    offset: int = 0,
    retry_times: int = 3,
    retry_delay: float = 0.1
) -> List[Dict]:
    """
    获取内容列表（带缓存+分布式锁）
    
    策略：
    1. 先查缓存，命中直接返回
    2. 缓存未命中，获取分布式锁
    3. 二次检查缓存
    4. 锁持有者查库并更新缓存
    5. 释放锁
    """
    cache_key = f"cache:content:list:{limit}:{offset}"
    lock_key = f"lock:content:list:{limit}:{offset}"
    
    # 1. 先查缓存
    try:
        cached_data = r.get_str(cache_key)
        if cached_data:
            logger.debug(f"[缓存命中] 内容列表 limit={limit}, offset={offset}")
            return json.loads(cached_data)
    except Exception as e:
        logger.warning(f"读取缓存失败: {e}")
    
    # 2. 缓存未命中，尝试获取锁
    lock_value = None
    for attempt in range(retry_times):
        lock_value = r.acquire_lock(lock_key, expire_seconds=10)
        if lock_value:
            logger.debug(f"[获取锁成功] 锁标识：{lock_value[:8]}...")
            break
        # 获取锁失败，随机延迟后重试（避免惊群效应）
        delay = retry_delay + random.uniform(0, 0.1)
        time.sleep(delay)
    
    if not lock_value:
        # 重试次数耗尽，直接查库（降级处理）
        logger.warning(f"[获取锁失败] 重试次数耗尽，直接查库")
        contents = get_contents_from_db(db, limit, offset)
        result = []
        for content in contents:
            try:
                author = db.query(User).filter(User.id == content.user_id).first()
                result.append(format_content_result(content, author))
            except Exception as e:
                logger.error(f"处理内容 {content.id} 时出错: {e}")
                continue
        return result
    
    try:
        # 3. 二次检查缓存（防止等待锁期间其他线程已更新缓存）
        try:
            cached_data = r.get_str(cache_key)
            if cached_data:
                logger.debug(f"[二次检查缓存命中] 内容列表 limit={limit}, offset={offset}")
                return json.loads(cached_data)
        except Exception as e:
            logger.warning(f"二次检查缓存失败: {e}")
        
        # 4. 锁持有者查询数据库
        contents = get_contents_from_db(db, limit, offset)
        
        # 格式化结果
        result = []
        for content in contents:
            try:
                author = db.query(User).filter(User.id == content.user_id).first()
                result.append(format_content_result(content, author))
            except Exception as e:
                logger.error(f"处理内容 {content.id} 时出错: {e}")
                continue
        
        # 5. 更新缓存（缓存5分钟）
        try:
            cache_seconds = 300  # 5分钟
            r.setex_str(cache_key, cache_seconds, json.dumps(result))
            logger.debug(f"[缓存更新] 内容列表已写入缓存，有效期 {cache_seconds} 秒")
        except Exception as e:
            logger.warning(f"更新缓存失败: {e}")
        
        return result
        
    finally:
        # 6. 释放锁
        if lock_value:
            success = r.release_lock(lock_key, lock_value)
            if success:
                logger.debug(f"[释放锁成功] 锁标识：{lock_value[:8]}...")
            else:
                logger.warning(f"[释放锁失败] 锁标识：{lock_value[:8]}...")


@router.get("/list")
async def get_content_list(
    limit: int = 20,
    offset: int = 0,
    db: Session = Depends(get_db)
):
    """
    获取内容列表（使用Redis缓存+分布式锁优化）
    
    参数:
        limit: 返回数量限制
        offset: 偏移量
    
    优化说明:
        - 使用Redis缓存热点数据，减少数据库查询
        - 使用分布式锁防止缓存击穿（多个请求同时查库）
        - 缓存过期时间：5分钟
    """
    try:
        result = get_content_list_with_cache(db, limit, offset)
        
        return JSONResponse({
            "success": True,
            "data": result,
            "count": len(result)
        })
        
    except Exception as e:
        logger.error(f"获取内容列表失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"获取失败: {str(e)}"
        }, status_code=500)

@router.get("/user/posts")
async def get_user_posts(
    limit: int = 20,
    offset: int = 0,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    """
    获取当前用户发布的内容列表
    
    参数:
        limit: 返回数量限制
        offset: 偏移量
    
    功能:
        - 返回当前用户发布的所有内容
        - 支持分页
    """
    try:
        if not current_user:
            return JSONResponse({
                "success": False,
                "message": "请先登录"
            }, status_code=401)
        
        logger.info(f"[用户内容列表] 用户 {current_user.id} 请求自己的内容列表, limit={limit}, offset={offset}")
        
        # 优化：先查询ID，再查询详情，避免大表排序内存问题
        # 查询用户发布的内容ID（只查询ID，减少内存占用）
        content_ids_query = db.query(Content.id).filter(
            Content.user_id == current_user.id,
            Content.status == 1
        ).order_by(
            Content.id.desc()  # 使用ID排序，通常ID有索引，比时间排序更高效
        ).limit(limit).offset(offset)
        
        content_ids = [row[0] for row in content_ids_query.all()]
        
        if not content_ids:
            return JSONResponse({
                "success": True,
                "data": [],
                "count": 0
            })
        
        # 根据ID查询完整内容（使用IN查询，避免排序）
        contents = db.query(Content).filter(
            Content.id.in_(content_ids),
            Content.status == 1
        ).all()
        
        # 按照ID列表顺序排序（保持原有顺序）
        content_dict = {c.id: c for c in contents}
        contents = [content_dict[cid] for cid in content_ids if cid in content_dict]
        
        # 格式化结果
        result = []
        for content in contents:
            # 查询作者信息（就是当前用户）
            result.append(format_content_result(content, current_user))
        
        logger.info(f"[用户内容列表] 用户 {current_user.id} 内容列表查询成功，共 {len(result)} 条")
        return JSONResponse({
            "success": True,
            "data": result,
            "count": len(result)
        })
        
    except Exception as e:
        logger.error(f"获取用户内容列表失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"获取失败: {str(e)}"
        }, status_code=500)


@router.get("/{content_id}")
async def get_content_detail(
    content_id: int,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    """
    获取内容详情
    
    参数:
        content_id: 内容ID
    
    功能:
        - 返回内容的详细信息
        - 如果用户已登录，返回用户的互动状态（是否点赞/收藏）
    """
    try:
        # 查询内容
        content = db.query(Content).filter(
            Content.id == content_id,
            Content.status == 1
        ).first()
        
        if not content:
            return JSONResponse({
                "success": False,
                "message": "内容不存在"
            }, status_code=404)
        
        # 查询作者信息
        author = db.query(User).filter(User.id == content.user_id).first()
        
        # 格式化内容
        result = format_content_result(content, author)
        
        # 如果用户已登录，查询互动状态
        user_interaction = None
        if current_user:
            # 查询是否点赞
            like_interaction = db.query(Interaction).filter(
                Interaction.user_id == current_user.id,
                Interaction.content_id == content_id,
                Interaction.type == "like"
            ).first()
            
            # 查询是否收藏
            collect_interaction = db.query(Interaction).filter(
                Interaction.user_id == current_user.id,
                Interaction.content_id == content_id,
                Interaction.type == "collect"
            ).first()
            
            user_interaction = {
                "is_liked": like_interaction is not None,
                "is_collected": collect_interaction is not None
            }
        
        result["user_interaction"] = user_interaction
        
        # 增加浏览次数
        content.view_count = (content.view_count or 0) + 1
        db.commit()
        result["view_count"] = content.view_count
        
        return JSONResponse({
            "success": True,
            "data": result
        })
        
    except Exception as e:
        logger.error(f"获取内容详情失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"获取失败: {str(e)}"
        }, status_code=500)


@router.get("/user/collections")
async def get_user_collections(
    limit: int = 20,
    offset: int = 0,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    """
    获取当前用户收藏的内容列表
    
    参数:
        limit: 返回数量限制
        offset: 偏移量
    
    功能:
        - 返回当前用户收藏的所有内容
        - 支持分页
    """
    try:
        if not current_user:
            return JSONResponse({
                "success": False,
                "message": "请先登录"
            }, status_code=401)
        
        logger.info(f"[用户收藏列表] 用户 {current_user.id} 请求收藏列表, limit={limit}, offset={offset}")
        
        # 查询用户收藏的内容ID列表（通过Interaction表）
        collect_interactions = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.type == "collect"
        ).order_by(
            Interaction.created_at.desc()
        ).limit(limit).offset(offset).all()
        
        # 获取内容ID列表
        content_ids = [interaction.content_id for interaction in collect_interactions]
        
        if not content_ids:
            return JSONResponse({
                "success": True,
                "data": [],
                "count": 0
            })
        
        # 查询内容详情（使用IN查询，避免排序内存问题）
        contents = db.query(Content).filter(
            Content.id.in_(content_ids),
            Content.status == 1
        ).all()
        
        # 按照收藏时间排序
        content_dict = {c.id: c for c in contents}
        sorted_contents = [content_dict[cid] for cid in content_ids if cid in content_dict]
        
        # 格式化结果
        result = []
        for content in sorted_contents:
            author = db.query(User).filter(User.id == content.user_id).first()
            result.append(format_content_result(content, author))
        
        logger.info(f"[用户收藏列表] 用户 {current_user.id} 收藏列表查询成功，共 {len(result)} 条")
        return JSONResponse({
            "success": True,
            "data": result,
            "count": len(result)
        })
        
    except Exception as e:
        logger.error(f"获取用户收藏列表失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"获取失败: {str(e)}"
        }, status_code=500)


@router.get("/user/likes")
async def get_user_likes(
    limit: int = 20,
    offset: int = 0,
    db: Session = Depends(get_db),
    current_user: Optional[User] = Depends(get_current_user_optional)
):
    """
    获取当前用户点赞的内容列表
    
    参数:
        limit: 返回数量限制
        offset: 偏移量
    
    功能:
        - 返回当前用户点赞的所有内容
        - 支持分页
    """
    try:
        if not current_user:
            return JSONResponse({
                "success": False,
                "message": "请先登录"
            }, status_code=401)
        
        logger.info(f"[用户点赞列表] 用户 {current_user.id} 请求点赞列表, limit={limit}, offset={offset}")
        
        # 查询用户点赞的内容ID列表（通过Interaction表）
        like_interactions = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.type == "like"
        ).order_by(
            Interaction.created_at.desc()
        ).limit(limit).offset(offset).all()
        
        # 获取内容ID列表
        content_ids = [interaction.content_id for interaction in like_interactions]
        
        if not content_ids:
            return JSONResponse({
                "success": True,
                "data": [],
                "count": 0
            })
        
        # 查询内容详情（使用IN查询，避免排序内存问题）
        contents = db.query(Content).filter(
            Content.id.in_(content_ids),
            Content.status == 1
        ).all()
        
        # 按照点赞时间排序
        content_dict = {c.id: c for c in contents}
        sorted_contents = [content_dict[cid] for cid in content_ids if cid in content_dict]
        
        # 格式化结果
        result = []
        for content in sorted_contents:
            author = db.query(User).filter(User.id == content.user_id).first()
            result.append(format_content_result(content, author))
        
        logger.info(f"[用户点赞列表] 用户 {current_user.id} 点赞列表查询成功，共 {len(result)} 条")
        return JSONResponse({
            "success": True,
            "data": result,
            "count": len(result)
        })
        
    except Exception as e:
        logger.error(f"获取用户点赞列表失败: {e}")
        import traceback
        traceback.print_exc()
        return JSONResponse({
            "success": False,
            "message": f"获取失败: {str(e)}"
        }, status_code=500)


@router.post("/{content_id}/like")
async def like_content(
    content_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """点赞内容"""
    try:
        content = db.query(Content).filter(Content.id == content_id).first()
        if not content:
            return JSONResponse({"success": False, "message": "内容不存在"}, status_code=404)
        
        existing = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.content_id == content_id,
            Interaction.type == "like"
        ).first()
        
        if existing:
            return JSONResponse({"success": False, "message": "已经点赞过了"}, status_code=400)
        
        interaction = Interaction(user_id=current_user.id, content_id=content_id, type="like")
        db.add(interaction)
        content.like_count = (content.like_count or 0) + 1
        db.commit()
        return JSONResponse({"success": True, "message": "点赞成功"})
    except Exception as e:
        db.rollback()
        logger.error(f"点赞失败: {e}")
        return JSONResponse({"success": False, "message": f"点赞失败: {str(e)}"}, status_code=500)


@router.post("/{content_id}/unlike")
async def unlike_content(
    content_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """取消点赞"""
    try:
        interaction = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.content_id == content_id,
            Interaction.type == "like"
        ).first()
        
        if not interaction:
            return JSONResponse({"success": False, "message": "未点赞"}, status_code=400)
        
        db.delete(interaction)
        content = db.query(Content).filter(Content.id == content_id).first()
        if content:
            content.like_count = max((content.like_count or 0) - 1, 0)
        db.commit()
        return JSONResponse({"success": True, "message": "取消点赞成功"})
    except Exception as e:
        db.rollback()
        logger.error(f"取消点赞失败: {e}")
        return JSONResponse({"success": False, "message": f"取消点赞失败: {str(e)}"}, status_code=500)


@router.post("/{content_id}/collect")
async def collect_content(
    content_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """收藏内容"""
    try:
        content = db.query(Content).filter(Content.id == content_id).first()
        if not content:
            return JSONResponse({"success": False, "message": "内容不存在"}, status_code=404)
        
        existing = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.content_id == content_id,
            Interaction.type == "collect"
        ).first()
        
        if existing:
            return JSONResponse({"success": False, "message": "已经收藏过了"}, status_code=400)
        
        interaction = Interaction(user_id=current_user.id, content_id=content_id, type="collect")
        db.add(interaction)
        content.collect_count = (content.collect_count or 0) + 1
        db.commit()
        return JSONResponse({"success": True, "message": "收藏成功"})
    except Exception as e:
        db.rollback()
        logger.error(f"收藏失败: {e}")
        return JSONResponse({"success": False, "message": f"收藏失败: {str(e)}"}, status_code=500)


@router.post("/{content_id}/uncollect")
async def uncollect_content(
    content_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """取消收藏"""
    try:
        interaction = db.query(Interaction).filter(
            Interaction.user_id == current_user.id,
            Interaction.content_id == content_id,
            Interaction.type == "collect"
        ).first()
        
        if not interaction:
            return JSONResponse({"success": False, "message": "未收藏"}, status_code=400)
        
        db.delete(interaction)
        content = db.query(Content).filter(Content.id == content_id).first()
        if content:
            content.collect_count = max((content.collect_count or 0) - 1, 0)
        db.commit()
        return JSONResponse({"success": True, "message": "取消收藏成功"})
    except Exception as e:
        db.rollback()
        logger.error(f"取消收藏失败: {e}")
        return JSONResponse({"success": False, "message": f"取消收藏失败: {str(e)}"}, status_code=500)


@router.get("/{content_id}/comments")
async def get_content_comments(
    content_id: int,
    limit: int = 20,
    offset: int = 0,
    db: Session = Depends(get_db)
):
    """获取内容评论列表"""
    try:
        comments = db.query(Interaction).filter(
            Interaction.content_id == content_id,
            Interaction.type == "comment",
            Interaction.parent_id == None
        ).order_by(Interaction.created_at.desc()).limit(limit).offset(offset).all()
        
        result = []
        for comment in comments:
            author = db.query(User).filter(User.id == comment.user_id).first()
            result.append({
                "id": comment.id,
                "content_id": comment.content_id,
                "comment_content": comment.comment_content,
                "author_id": comment.user_id,
                "author_nickname": author.nickname if author else "游客",
                "author_avatar": author.avatar_url if author else None,
                "created_at": comment.created_at.isoformat() if comment.created_at else None
            })
        
        return JSONResponse({"success": True, "data": result, "count": len(result)})
    except Exception as e:
        logger.error(f"获取评论列表失败: {e}")
        return JSONResponse({"success": False, "message": f"获取失败: {str(e)}"}, status_code=500)


@router.post("/{content_id}/comment")
async def comment_content(
    content_id: int,
    body: dict,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """评论内容"""
    try:
        content_text = body.get('content', '').strip()
        if not content_text:
            return JSONResponse({"success": False, "message": "评论内容不能为空"}, status_code=400)
        
        content = db.query(Content).filter(Content.id == content_id).first()
        if not content:
            return JSONResponse({"success": False, "message": "内容不存在"}, status_code=404)
        
        interaction = Interaction(
            user_id=current_user.id,
            content_id=content_id,
            type="comment",
            comment_content=content_text
        )
        db.add(interaction)
        content.comment_count = (content.comment_count or 0) + 1
        db.commit()
        db.refresh(interaction)
        
        return JSONResponse({
            "success": True,
            "message": "评论成功",
            "data": {
                "id": interaction.id,
                "comment_content": interaction.comment_content,
                "created_at": interaction.created_at.isoformat() if interaction.created_at else None
            }
        })
    except Exception as e:
        db.rollback()
        logger.error(f"评论失败: {e}")
        return JSONResponse({"success": False, "message": f"评论失败: {str(e)}"}, status_code=500)


